////////////////////////////////////////////////////////////////////////////
//	WinMain.cpp
//		Entry point of the program - defines the window class, creates the
//		window and handles the Windows message loop
//
//	Author:					Daniel Lima - March 2012
//	Partially based on:		David Brown's WinMain from Full Sail University
//	Special thanks:			Doug Monroe - for introducing me to C++
//							Robert Muscarella - for introducing me to the
//							basics of programming with the Windows API
/////////////////////////////////////////////////////////////////////////////

// Include the Windows API using Lean and Mean to exclude rarely
// used stuff from its headers
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

// If the project is being built on Debug mode, this will set the program's
// entry point to Console "main" instead of Windows "WinMain", so there will
// be a console window to help debug stuff.
#ifdef _DEBUG
#pragma comment(linker, "/SUBSYSTEM:Console")
const bool g_bFULLSCREEN = false;
#else
#pragma comment(linker, "/SUBSYSTEM:Windows")
const bool g_bFULLSCREEN = true;
#endif
// TODO: Change the above code to an actual property to make the code cleaner


// Necessary to be able to easily print data to the console window
#include <iostream>
#ifdef _DEBUG
#define LOG(info) std::cout << info << std::endl;
#else
#define LOG(info)
#endif

// Using multi-byte character set
#include <tchar.h>

// Globals for window properties
LPTSTR		g_szWINDOW_CLASS	= _T("WindowClass");
LPTSTR		g_szWINDOW_TITLE	= _T("Project Notebook");
const int	g_nWINDOW_WIDTH		= 640;
const int	g_nWINDOW_HEIGHT	= 480;

// Our game class
#include "GameCore.h"

// To handle message input
#include "Input/InputManager.h"

// Integer to return if actions fail or succeed
#define _FAILED		0
#define _SUCCEEDED	1

// Functions to register and create the window
ATOM WINAPI RegisterWindow(HINSTANCE hInstance);
HWND MakeWindow(HINSTANCE hInstance);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Console "main" - only called in Debug Mode
//		Calls Windows' WinMain
int main(int argc, char* argv[])
{
	std::cout << "Running " << GetCommandLine() << " in Debug Mode\n\n";
	std::cout << "Debug Log:\n------------\n\n";
	_tWinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOWDEFAULT);


	LOG("Main finished.\n");
	system("pause");
	return 0;
}

// Main function
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	// Will be used to test if the functions succeeded
	HRESULT hResult = false;
	HWND hWnd = nullptr;

	// Register and create our window
	hResult = RegisterWindow(hInstance);
	if(!hResult) return _FAILED;
	LOG("Window class registered.");

	hWnd = MakeWindow(hInstance);
	if(!hWnd) return _FAILED;
	LOG("Window created.");


	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	// Initialize the input
	hResult = InputManager::GetInstance()->Initialize(hWnd);
	if(!hResult) return _FAILED;
	LOG("Input Manager initialized.");

	// Initialize the game
	hResult = GameCore::GetInstance()->Initialize(hWnd, hInstance, g_nWINDOW_WIDTH, g_nWINDOW_HEIGHT, g_bFULLSCREEN);
	if(!hResult) return _FAILED;
	LOG("Game initialized.");

	// Main message loop
	MSG Message = {};
	while(GetMessage(&Message, nullptr, 0, 0))
	{
		// Peek message and dispatch it to the message proc
		TranslateMessage(&Message);
		DispatchMessage(&Message);

		// Run game
		hResult = GameCore::GetInstance()->Main();
		if(!hResult) PostQuitMessage(0);
	}

	// Exit the game
	GameCore::GetInstance()->DeleteInstance();
	LOG("Game finished.");
	InputManager::GetInstance()->Shutdown();
	LOG("Input Manager shut down.");

	// Unregister window class and return
	UnregisterClass(g_szWINDOW_CLASS, hInstance);
	return (int)Message.wParam;
}

ATOM WINAPI RegisterWindow(HINSTANCE hInstance)
{
	WNDCLASSEX WindowClass;

	WindowClass.cbSize			= sizeof(WindowClass);
	WindowClass.style			= CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	WindowClass.lpfnWndProc		= WindowProc;
	WindowClass.cbClsExtra		= 0;
	WindowClass.cbWndExtra		= 0;
	WindowClass.hInstance		= hInstance;
	WindowClass.hIcon			= LoadIcon(hInstance, nullptr);
	WindowClass.hIconSm			= LoadIcon(hInstance, nullptr);
	WindowClass.hCursor			= LoadCursor(NULL, IDC_ARROW);
	WindowClass.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	WindowClass.lpszMenuName	= nullptr; 
	WindowClass.lpszClassName	= g_szWINDOW_CLASS;

	return RegisterClassEx(&WindowClass);
}

HWND MakeWindow(HINSTANCE hInstance)
{
	DWORD dwWindowStyleFlags = WS_VISIBLE;
	dwWindowStyleFlags |= (g_bFULLSCREEN ? WS_POPUP : WS_OVERLAPPEDWINDOW);

	RECT rClientArea  = { 0, 0, g_nWINDOW_WIDTH, g_nWINDOW_HEIGHT };
	AdjustWindowRectEx(&rClientArea, dwWindowStyleFlags, false, WS_EX_APPWINDOW);

	int nWindowWidth	= rClientArea.right - rClientArea.left;
	int nWindowHeight	= rClientArea.bottom - rClientArea.top;

	return CreateWindowEx(0, g_szWINDOW_CLASS, g_szWINDOW_TITLE, dwWindowStyleFlags,										
		(GetSystemMetrics(SM_CXSCREEN)/2) - (nWindowWidth/2),
		(GetSystemMetrics(SM_CYSCREEN)/2) - (nWindowHeight/2),
		nWindowWidth, nWindowHeight, HWND_DESKTOP, nullptr, hInstance, nullptr);	
}

LRESULT CALLBACK WindowProc(HWND hWnd, unsigned int uiMessage, WPARAM wParam, LPARAM lParam)
{
	switch(uiMessage)
	{
	case WM_PAINT:
		ValidateRect(hWnd, nullptr); 
		break;
	case WM_CLOSE:
		DestroyWindow(hWnd);			
		break;

	case WM_ACTIVATE:
		//	gaining focus
		if (LOWORD(wParam) != WA_INACTIVE)
		{
			// unpause game code here
		}
		else // losing focus
		{
			// pause game code here
		}
		break;

	case WM_CREATE: 
		//	Do initialization here
		break;

	case WM_DESTROY: 
		//	Kill the application
		PostQuitMessage(0);
		break;

	default:
		// Send this message to the input manager
		if(InputManager::GetInstance()->ProcessMessage(uiMessage, wParam, lParam) == 0)
			break;
		else
			return DefWindowProc(hWnd, uiMessage, wParam, lParam);
	}

	return 0;
}
